package com.code.wifi.control;


import android.content.Context;
import android.net.ConnectivityManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.util.Log;

import com.code.wifi.listener.ActionListener;
import com.code.wifi.listener.OnWifiConnectListener;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

/**
 * Created by ZJM on 2017/3/30.
 * <p>
 * WIFI 控制类 用于控制 WIFI 的扫描，连接，断开，检测 WIFI 的配置情况
 */

public class WifiControl {
    private static final String TAG = "WifiControl";
    private WifiManager manager;//Wifi管理器对象
    private static WifiControl controlInstance = null;//Wifi控制类实例
    private OnWifiConnectListener wifiListener;//连接监听接口
    private ConnectivityManager connectivityManager;//系统连接管理对象

    /**
     * 构造函数，单例模式
     *
     * @param context 上下文
     */
    private WifiControl(Context context) {
        manager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    }

    /**
     * 获取wifi控制类单利
     *
     * @param context 上下文
     * @return WifiControl instance
     */
    public static WifiControl getInstance(Context context) {
        synchronized (WifiControl.class) {
            if (controlInstance == null) {
                controlInstance = new WifiControl(context);
            }
        }
        return controlInstance;
    }

    /**
     * 判断 Wifi 连接状态
     */
    public boolean isConnected() {
        return connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected();
    }

    /**
     * 扫描 wifi
     */
    public void launchScan() {
        if (!isWifiEnable()) {//判断wifi状态
            Log.i(TAG, "---Wifi没有开启---");
            return;
        }
        manager.startScan();
    }

    /**
     * 判断wifi是否开启，true开启，false关闭
     *
     * @return boolean
     */
    public boolean isWifiEnable() {
        return manager.isWifiEnabled();
    }

    /**
     * 启动 或者 关闭 wifi
     */
    public void openOrCloseWifi() {
        if (!isWifiEnable()) {
            manager.setWifiEnabled(true);
        } else {
            manager.setWifiEnabled(false);
        }
    }

    /**
     * 获取扫描完成后的wifi列表
     *
     * @return wifi list
     */
    public List<ScanResult> getWifiScanResult() {
        return manager.getScanResults();
    }

    /**
     * 判断当前配置是否已经存在于系统中
     *
     * @param checkConfigured 用户新定义的配置
     * @return If configure is exists, return the configure which is updated, or else return null.
     */
    public WifiConfiguration isExists(WifiConfiguration checkConfigured) {
        //获取已配置的wifi信息，以 list 的信息存储
        List<WifiConfiguration> configuredNetWorks = manager.getConfiguredNetworks();
        //遍历list, 判断目标 configuration 是否存在
        for (WifiConfiguration configuredNetWork : configuredNetWorks) {
            if (configuredNetWork.SSID.equals(checkConfigured.SSID)) {
                Log.i(TAG, "---系统保存配置的 SSID :" + configuredNetWork.SSID + " netWorkId :" + configuredNetWork.networkId);
                checkConfigured.networkId = configuredNetWork.networkId;//用于后继更新配置
                return checkConfigured;
            }
        }
        return null;
    }


    /**
     * 获取NetWorkId
     *
     * @param scanResult 指定扫描到的wifi信息
     * @return If the scanResult is configure return networkId, or else return -1
     */
    public int getNetWorkIdFromConfigure(ScanResult scanResult) {
        String SSID = String.format("\"%s\"", scanResult.SSID);
        //获取当前系统已配置的 wifi 信息，存放于list中
        List<WifiConfiguration> configuredNetWorks = manager.getConfiguredNetworks();
        //遍历list, 读取匹配的 configuration 的 networkId
        for (WifiConfiguration configuredNetWork : configuredNetWorks) {
            if (configuredNetWork.SSID.equals(SSID)) {
                return configuredNetWork.networkId;
            }
        }
        return -1;
    }

    /**
     * 通过密码连接 wifi
     *
     * @param scanResult 要连接的wifi
     * @param pwd        密码
     * @param listener   连接的监听
     */
    public void connectWifiByPwd(ScanResult scanResult, String pwd, OnWifiConnectListener listener) {
        //SSID
//        String SSID = String.format("\"%s\"", scanResult.SSID);

        String SSID = scanResult.SSID;
        //加密方式
        SecurityMode securityMode = getSecurityMode(scanResult);

        //创建配置信息
        WifiConfiguration addConfigure = createWifiConfiguration(SSID, pwd, securityMode);

        int networkId;
        WifiConfiguration updateConfigure = isExists(addConfigure);
        //对配置信息的处理，存在更新，不存在添加
        if (null != updateConfigure) {
            //更新配置
            networkId = manager.updateNetwork(updateConfigure);
        } else {
            //添加配置
            networkId = manager.addNetwork(addConfigure);
        }
        //通过networkId 连接 wifi
        connectWifiByNetworkId(scanResult, networkId, listener);
    }


    /**
     * 通过networkId连接 wifi
     *
     * @param scanResult 要连接的wifi
     * @param networkId  NetworkId
     * @param listener   连接的监听
     */
    public void connectWifiByNetworkId(ScanResult scanResult, int networkId, OnWifiConnectListener listener) {
        //连接的回调监听接口
        wifiListener = listener;
        //连接开始时回调
        listener.onStart(scanResult);

        /**
         * 判断 networkId 是否有效
         * -1 表示 wifi 的 configuration 参数不正确 获取失败返回-1
         */
        if (-1 == networkId) {
            //wifi 连接失败
            if (null != wifiListener) {
                //配置错误
                wifiListener.onFailure(scanResult);
                //连接完成
                wifiListener.onFinished();
                wifiListener = null;
            }
            return;
        }

        //获取当前的连接的网络 断开当前连接的网络
        WifiInfo wifiInfo = getConnectionInfo();
        if (null != wifiInfo) {
            boolean isDisconnect = disconnectWifi(wifiInfo.getNetworkId());
            if (!isDisconnect) {
                //断开当前网络失败
                if (null != wifiListener) {
                    //断开当前网络失败
                    wifiListener.onFailure(scanResult);
                    //连接完成
                    wifiListener.onFinished();
                    wifiListener = null;
                }
                return;
            }
        }

        //连接 wifi
        boolean isEnable = manager.enableNetwork(networkId, true);
        Log.i(TAG, "---isEnable " + isEnable);
        if (!isEnable) {
            //连接失败
            if (null != wifiListener) {
                //连接失败
                wifiListener.onFailure(scanResult);
                //连接完成
                wifiListener.onFinished();
                wifiListener = null;
            }
        }


    }

    /**
     * 生成新的配置信息，用于连接wifi
     *
     * @param SSID         wifi 的名字
     * @param pwd          wifi 的密码
     * @param securityMode wifi 加密方式
     * @return The wifi configured
     */
    public WifiConfiguration createWifiConfiguration(String SSID, String pwd, SecurityMode securityMode) {
        WifiConfiguration config = new WifiConfiguration();
        config.allowedAuthAlgorithms.clear();
        config.allowedGroupCiphers.clear();
        config.allowedKeyManagement.clear();
        config.allowedPairwiseCiphers.clear();
        config.allowedProtocols.clear();
        config.SSID = "\"" + SSID + "\"";
        if (securityMode == SecurityMode.WEP) {
            config.hiddenSSID = true;
            config.wepKeys[0] = "\"" + pwd + "\"";
            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
            config.wepTxKeyIndex = 0;
        } else if (securityMode == SecurityMode.WPA) {
            config.preSharedKey = "\"" + pwd + "\"";
            config.hiddenSSID = true;
            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
            config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
            config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
            config.status = WifiConfiguration.Status.ENABLED;
        } else if (securityMode == SecurityMode.OPEN) {
            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
        }
        return config;
    }

    /**
     * 断开 wifi
     *
     * @param networkId networkId
     * @return {@code true} If the operation succeeded
     */
    public boolean disconnectWifi(int networkId) {
        //根据 networkId 断开wifi
        boolean isDisable = manager.disableNetwork(networkId);
        boolean isDisconnect = manager.disconnect();
        return isDisable && isDisconnect;
    }

    /**
     * 获取当前正在连接的 wifi 信息
     *
     * @return The information of currently wifi connected
     */
    public WifiInfo getConnectionInfo() {
        try {
            return manager.getConnectionInfo();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 枚举，表示wifi的加密方式
     */
    public enum SecurityMode {
        OPEN, WEP, WPA, WPA2
    }

    /**
     * 获取指定 wifi 的加密方式
     *
     * @param scanResult 指定的wifi
     * @return 加密方式
     */
    public SecurityMode getSecurityMode(ScanResult scanResult) {
        String capabilities = scanResult.capabilities;
        Log.i(TAG, "--getSecurityMode --- capabilities" + capabilities);
        //capabilities的格式为 [WPA][ESS]
        if (capabilities.contains("WPA")) {
            return SecurityMode.WPA;
        } else if (capabilities.contains("WEP")) {
            return SecurityMode.WEP;
        } else if (capabilities.contains("WPA2")) {
            return SecurityMode.WPA2;
        } else {
            // 没有加密
            return SecurityMode.OPEN;
        }
    }

    /**
     * 关闭 wifi
     */
    public void closeWifi() {
        if (manager.isWifiEnabled()) {
            manager.setWifiEnabled(false);
        }
    }

    /**
     * 删除 wifi
     * 不懂 暂时保留
     *
     * @param networkId netWorkId
     * @param listener  ActionListener
     */
    public void forget(int networkId, ActionListener listener) {
        //删除wifi配置需要调用 forget 方法
        //但是这个方法是 hide 的，在源码中，会发现有@hide的标志
        //而 ActionListener 前面也有 hide
        //这里使用【反射机制】获取 ActionListener 的字节码对象
        Class<?> actionListenerClazz = null;
        try {
            //其中 Log 输出信息只是用于查看获取的方法的准确性
            actionListenerClazz = Class.forName("android.net..wifi.WifiManager$ActionListener");
            Log.i(TAG, "---name = " + actionListenerClazz.getName());
            Method[] declaredMethods = actionListenerClazz.getMethods();
            Log.i(TAG, "---method size =" + declaredMethods.length);
            for (int i = 0; i < declaredMethods.length; i++) {
                Log.i(TAG, "---method name = " + declaredMethods[i]);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //若反射对象为空表示获取失败
        if (actionListenerClazz == null) {
            throw new RuntimeException("Fail to get ActionListener...");
        }

        Class<? extends WifiManager> wifiClazz = manager.getClass();
        try {
            Method forget = wifiClazz.getDeclaredMethod("forget", int.class, actionListenerClazz);
            Log.i(TAG, "---method name =" + forget);

            //执行方法
            forget.invoke(manager, new Object[]{networkId, listener});
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            Log.e(TAG, "---exception = " + e);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        //如果直接放在系统中编译的话，那么直接调用 WifiManager 的 forget 方法即可
    }


}
